package com.newlink.building.utils

import android.telecom.CallAudioState
import com.newlink.building.compatibility.Module_Phone_Compatibility
import com.newlink.building.manager.Module_Phone_VoipController
import org.linphone.core.AudioDevice
import org.linphone.core.Call
import org.linphone.core.tools.Log

/**
 * Created by xiaodong on 2022/8/8.
 */
class Module_Phone_AudioRouteUtils {
    companion object {
        private fun applyAudioRouteChange(
            call: Call?,
            types: List<AudioDevice.Type>,
            output: Boolean = true
        ) {
            val listSize = types.size
            val stringBuilder = StringBuilder()
            var index = 0
            while (index < listSize) {
                stringBuilder.append(types[index].name)
                if (index < listSize - 1) {
                    stringBuilder.append("/")
                }
                index++
            }
            val typesNames = stringBuilder.toString()

            if (Module_Phone_VoipController.get().coreContext.core.callsNb == 0) {
                Log.e("[Audio Route Helper] No call found, aborting [$typesNames] audio route change")
                return
            }
            val currentCall = call ?: Module_Phone_VoipController.get().coreContext.core.currentCall
            ?: Module_Phone_VoipController.get().coreContext.core.calls[0]
            val conference = Module_Phone_VoipController.get().coreContext.core.conference
            val capability = if (output)
                AudioDevice.Capabilities.CapabilityPlay
            else
                AudioDevice.Capabilities.CapabilityRecord
            val preferredDriver = if (output) {
                Module_Phone_VoipController.get().coreContext.core.defaultOutputAudioDevice.driverName
            } else {
                Module_Phone_VoipController.get().coreContext.core.defaultInputAudioDevice.driverName
            }

            val extendedAudioDevices = Module_Phone_VoipController.get().coreContext.core.extendedAudioDevices
            Log.i("[Audio Route Helper] Looking for an ${if (output) "output" else "input"} audio device with capability [$capability], driver name [$preferredDriver] and type [$types] in extended audio devices list (size ${extendedAudioDevices.size})")
            val foundAudioDevice = extendedAudioDevices.find {
                it.driverName == preferredDriver && types.contains(it.type) && it.hasCapability(
                    capability
                )
            }
            val audioDevice = if (foundAudioDevice == null) {
                Log.w("[Audio Route Helper] Failed to find an audio device with capability [$capability], driver name [$preferredDriver] and type [$types]")
                extendedAudioDevices.find {
                    types.contains(it.type) && it.hasCapability(capability)
                }
            } else {
                foundAudioDevice
            }

            if (audioDevice == null) {
                Log.e("[Audio Route Helper] Couldn't find audio device with capability [$capability] and type [$types]")
                for (device in extendedAudioDevices) {
                    // TODO: switch to debug?
                    Log.i("[Audio Route Helper] Extended audio device: [${device.deviceName} (${device.driverName}) ${device.type} / ${device.capabilities}]")
                }
                return
            }

            if (conference != null && conference.isIn) {
                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], routing conference audio to it")
                if (output) conference.outputAudioDevice = audioDevice
                else conference.inputAudioDevice = audioDevice
            } else if (currentCall != null) {
                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], routing call audio to it")
                if (output) currentCall.outputAudioDevice = audioDevice
                else currentCall.inputAudioDevice = audioDevice
            } else {
                Log.i("[Audio Route Helper] Found [${audioDevice.type}] ${if (output) "playback" else "recorder"} audio device [${audioDevice.deviceName} (${audioDevice.driverName})], changing core default audio device")
                if (output) Module_Phone_VoipController.get().coreContext.core.outputAudioDevice = audioDevice
                else Module_Phone_VoipController.get().coreContext.core.inputAudioDevice = audioDevice
            }
        }

        private fun changeCaptureDeviceToMatchAudioRoute(
            call: Call?,
            types: List<AudioDevice.Type>
        ) {
            when (types.first()) {
                AudioDevice.Type.Bluetooth -> {
                    if (isBluetoothAudioRecorderAvailable()) {
                        Log.i("[Audio Route Helper] Bluetooth device is able to record audio, also change input audio device")
                        applyAudioRouteChange(call, arrayListOf(AudioDevice.Type.Bluetooth), false)
                    }
                }

                AudioDevice.Type.Headset, AudioDevice.Type.Headphones -> {
                    if (isHeadsetAudioRecorderAvailable()) {
                        Log.i("[Audio Route Helper] Headphones/headset device is able to record audio, also change input audio device")
                        applyAudioRouteChange(
                            call,
                            (arrayListOf(AudioDevice.Type.Headphones, AudioDevice.Type.Headset)),
                            false
                        )
                    }
                }

                AudioDevice.Type.Earpiece, AudioDevice.Type.Speaker -> {
                    Log.i("[Audio Route Helper] Audio route requested to Earpice or speaker, setting input to Microphone")
                    applyAudioRouteChange(call, (arrayListOf(AudioDevice.Type.Microphone)), false)
                }

                else -> {}
            }
        }

        private fun routeAudioTo(
            call: Call?,
            types: List<AudioDevice.Type>,
            skipTelecom: Boolean = false
        ) {
            val currentCall = call ?: Module_Phone_VoipController.get().coreContext.core.currentCall
            ?: Module_Phone_VoipController.get().coreContext.core.calls.firstOrNull()
            if (currentCall != null && !skipTelecom && Module_Phone_TelecomHelper.exists()) {
                Log.i("[Audio Route Helper] Call provided & Telecom Helper exists, trying to dispatch audio route change through Telecom API")
                val connection = Module_Phone_TelecomHelper.get()
                    .findConnectionForCallId(currentCall.callLog.callId.orEmpty())
                if (connection != null) {
                    val route = when (types.first()) {
                        AudioDevice.Type.Earpiece -> CallAudioState.ROUTE_EARPIECE
                        AudioDevice.Type.Speaker -> CallAudioState.ROUTE_SPEAKER
                        AudioDevice.Type.Headphones, AudioDevice.Type.Headset -> CallAudioState.ROUTE_WIRED_HEADSET
                        AudioDevice.Type.Bluetooth, AudioDevice.Type.BluetoothA2DP -> CallAudioState.ROUTE_BLUETOOTH
                        else -> CallAudioState.ROUTE_WIRED_OR_EARPIECE
                    }
                    Log.i("[Audio Route Helper] Telecom Helper & matching connection found, dispatching audio route change through it")
                    // We will be called here again by NativeCallWrapper.onCallAudioStateChanged()
                    // but this time with skipTelecom = true
                    if (!Module_Phone_Compatibility.changeAudioRouteForTelecomManager(connection, route)) {
                        Log.w("[Audio Route Helper] Connection is already using this route internally, make the change!")
                        applyAudioRouteChange(currentCall, types)
                        changeCaptureDeviceToMatchAudioRoute(currentCall, types)
                    }
                } else {
                    Log.w("[Audio Route Helper] Telecom Helper found but no matching connection!")
                    applyAudioRouteChange(currentCall, types)
                    changeCaptureDeviceToMatchAudioRoute(currentCall, types)
                }
            } else {
                applyAudioRouteChange(call, types)
                changeCaptureDeviceToMatchAudioRoute(call, types)
            }
        }

        fun routeAudioToEarpiece(call: Call? = null, skipTelecom: Boolean = false) {
//            routeAudioTo(call, arrayListOf(AudioDevice.Type.Earpiece), skipTelecom)
        }

        fun routeAudioToSpeaker(call: Call? = null, skipTelecom: Boolean = false) {
//            routeAudioTo(call, arrayListOf(AudioDevice.Type.Speaker), skipTelecom)
        }

        fun routeAudioToBluetooth(call: Call? = null, skipTelecom: Boolean = false) {
//            routeAudioTo(call, arrayListOf(AudioDevice.Type.Bluetooth), skipTelecom)
        }

        fun routeAudioToHeadset(call: Call? = null, skipTelecom: Boolean = false) {
//            routeAudioTo(call, arrayListOf(AudioDevice.Type.Headphones, AudioDevice.Type.Headset), skipTelecom)
        }

        fun isSpeakerAudioRouteCurrentlyUsed(call: Call? = null): Boolean {
            if (Module_Phone_VoipController.get().coreContext.core.callsNb == 0) {
                Log.w("[Audio Route Helper] No call found, so speaker audio route isn't used")
                return false
            }
            val currentCall = call ?: Module_Phone_VoipController.get().coreContext.core.currentCall
            ?: Module_Phone_VoipController.get().coreContext.core.calls[0]
            val conference = Module_Phone_VoipController.get().coreContext.core.conference

            val audioDevice =
                if (conference != null && conference.isIn) conference.outputAudioDevice else currentCall.outputAudioDevice
            Log.i("[Audio Route Helper] Playback audio device currently in use is [${audioDevice?.deviceName}] with type (${audioDevice?.type})")
            return audioDevice?.type == AudioDevice.Type.Speaker
        }

        fun isBluetoothAudioRouteCurrentlyUsed(call: Call? = null): Boolean {
            if (Module_Phone_VoipController.get().coreContext.core.callsNb == 0) {
                Log.w("[Audio Route Helper] No call found, so bluetooth audio route isn't used")
                return false
            }
            val currentCall = call ?: Module_Phone_VoipController.get().coreContext.core.currentCall
            ?: Module_Phone_VoipController.get().coreContext.core.calls[0]
            val conference = Module_Phone_VoipController.get().coreContext.core.conference

            val audioDevice =
                if (conference != null && conference.isIn) conference.outputAudioDevice else currentCall.outputAudioDevice
            Log.i("[Audio Route Helper] Playback audio device currently in use is [${audioDevice?.deviceName}] with type (${audioDevice?.type})")
            return audioDevice?.type == AudioDevice.Type.Bluetooth
        }

        fun isHeadsetAudioRouteCurrentlyUsed(call: Call? = null): Boolean {
            if (Module_Phone_VoipController.get().coreContext.core.callsNb == 0) {
                Log.w("[Audio Route Helper] No call found, so speaker audio route isn't used")
                return false
            }
            val currentCall = call ?: Module_Phone_VoipController.get().coreContext.core.currentCall
            ?: Module_Phone_VoipController.get().coreContext.core.calls[0]
            val conference = Module_Phone_VoipController.get().coreContext.core.conference

            val audioDevice =
                if (conference != null && conference.isIn) conference.outputAudioDevice else currentCall.outputAudioDevice
            Log.i("[Audio Route Helper] Playback audio device currently in use is [${audioDevice?.deviceName}] with type (${audioDevice?.type})")
            return audioDevice?.type == AudioDevice.Type.Headset
        }


        fun isBluetoothAudioRouteAvailable(): Boolean {
            for (audioDevice in Module_Phone_VoipController.get().coreContext.core.audioDevices) {
                if (audioDevice.type == AudioDevice.Type.Bluetooth &&
                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
                ) {
                    Log.i("[Audio Route Helper] Found bluetooth audio device [${audioDevice.deviceName}]")
                    return true
                }
            }
            return false
        }

        private fun isBluetoothAudioRecorderAvailable(): Boolean {
            for (audioDevice in Module_Phone_VoipController.get().coreContext.core.audioDevices) {
                if (audioDevice.type == AudioDevice.Type.Bluetooth &&
                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
                ) {
                    Log.i("[Audio Route Helper] Found bluetooth audio recorder [${audioDevice.deviceName}]")
                    return true
                }
            }
            return false
        }

        fun isHeadsetAudioRouteAvailable(): Boolean {
            for (audioDevice in Module_Phone_VoipController.get().coreContext.core.audioDevices) {
                if ((audioDevice.type == AudioDevice.Type.Headset || audioDevice.type == AudioDevice.Type.Headphones) &&
                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityPlay)
                ) {
                    Log.i("[Audio Route Helper] Found headset/headphones audio device [${audioDevice.deviceName}]")
                    return true
                }
            }
            return false
        }

        private fun isHeadsetAudioRecorderAvailable(): Boolean {
            for (audioDevice in Module_Phone_VoipController.get().coreContext.core.audioDevices) {
                if ((audioDevice.type == AudioDevice.Type.Headset || audioDevice.type == AudioDevice.Type.Headphones) &&
                    audioDevice.hasCapability(AudioDevice.Capabilities.CapabilityRecord)
                ) {
                    Log.i("[Audio Route Helper] Found headset/headphones audio recorder [${audioDevice.deviceName}]")
                    return true
                }
            }
            return false
        }
    }
}